//package com.example.rwq_test.webrtc1;
//
//import com.google.gson.Gson;
//import com.google.gson.GsonBuilder;
//import com.google.gson.JsonObject;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import org.springframework.web.socket.CloseStatus;
//import org.springframework.web.socket.TextMessage;
//import org.springframework.web.socket.WebSocketSession;
//import org.springframework.web.socket.handler.TextWebSocketHandler;
//
//import java.io.IOException;
//import java.util.concurrent.ConcurrentHashMap;
//import java.util.concurrent.atomic.AtomicInteger;
//import java.util.stream.Collectors;
//
//public class PlayerHandler extends TextWebSocketHandler {
//
//    private static Logger log = LoggerFactory.getLogger(PlayerHandler.class);
//    private static final Gson gson = new GsonBuilder().create();
//    private final ConcurrentHashMap<String, UserSession> users = new ConcurrentHashMap<>();
//    private final AtomicInteger portCounter = new AtomicInteger(5000);
//
//    @Override
//    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
//        stop(session);
//    }
//
//    @Override
//    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
//        JsonObject jsonMessage = gson.fromJson(message.getPayload(), JsonObject.class);
//        log.debug("Incoming message for session {}: {}", session.getId(), jsonMessage);
//
//        String id = jsonMessage.get("id").getAsString();
//        log.info("Processing instruction ID: {} for session {}", id, session.getId());
//        switch (id) {
//            case "start":
//                start(session, jsonMessage);
//                break;
//            case "stop":
//                stop(session);
//                break;
//            case "onIceCandidate":
//                break;
//            case "loadAndPlay":
//                String filePath = jsonMessage.get("file").getAsString();
//                log.info("Received loadAndPlay for file: {} in session {}", filePath, session.getId());
//                loadFile(session, filePath);
//                play(session);
//                break;
//            case "pause":
//                log.info("Received pause for session {}", session.getId());
//                pause(session);
//                break;
//            default:
//                log.warn("Invalid ID: {} for session {}", id, session.getId());
//                sendError(session, "Invalid id: " + id);
//        }
//    }
//
//    private void start(WebSocketSession session, JsonObject jsonMessage) {
//        try {
//            UserSession user = new UserSession();
//            users.put(session.getId(), user);
//
//            JsonObject sdpOfferObj = jsonMessage.getAsJsonObject("sdpOffer");
//            if (sdpOfferObj == null) {
//                throw new IllegalArgumentException("Missing or invalid sdpOffer");
//            }
//            String sdpOffer = sdpOfferObj.get("sdp").getAsString();
//            if (sdpOffer == null) {
//                throw new IllegalArgumentException("Missing sdp in sdpOffer");
//            }
//
//            log.info("Incoming parsed successfully for session {}. Offer SDP (m-lines preview): {}", session.getId(), extractMLines(sdpOffer));
//
//            int audioPort = portCounter.getAndAdd(4);
//            int videoPort = audioPort + 2;
//            String sdpAnswer = generateSdpAnswer(audioPort, videoPort);
//            log.debug("Generated SDP Answer for session {}: {}", session.getId(), sdpAnswer);
//
//            JsonObject response = new JsonObject();
//            response.addProperty("id", "startResponse");
//            response.addProperty("sdpAnswer", sdpAnswer);
//            sendMessage(session, response);
//
//            user.setAudioPort(audioPort);
//            user.setVideoPort(videoPort);
//            log.info("Session {} started with audio port {} video port {}", session.getId(), audioPort, videoPort);
//        } catch (Throwable t) {
//            log.error("Start failed for session {}: {}", session.getId(), t.getMessage(), t);
//            sendError(session, t.getMessage());
//        }
//    }
//
//    // 加载文件：两个 FFmpeg 进程 (video VP8, audio Opus)
//    private void loadFile(WebSocketSession session, String filePath) {
//        UserSession user = users.get(session.getId());
//        if (user == null || (user.getVideoProcess() != null && user.getAudioProcess() != null)) {
//            log.warn("Cannot load file for session {}: user null or processes running", session.getId());
//            return;
//        }
//
//        try {
//            int audioPort = user.getAudioPort();
//            int videoPort = user.getVideoPort();
//            // 随机 SSRC (signed 32-bit 正值)
//            int videoSsrc = (int) (Math.random() * 0x7FFFFFFFL);
//            int audioSsrc = (int) (Math.random() * 0x7FFFFFFFL);
//
//            // 视频进程 (libvpx VP8, payload 96)
//            ProcessBuilder videoPb = new ProcessBuilder(
//                    "ffmpeg", "-re", "-i", filePath,
//                    "-map", "0:v", "-c:v", "libvpx", "-b:v", "500k", "-deadline", "realtime", "-cpu-used", "5",
//                    "-payload_type", "96", "-ssrc", String.valueOf(videoSsrc),
//                    "-f", "rtp", "rtp://127.0.0.1:" + videoPort
//            );
//            videoPb.redirectErrorStream(true);
//            Process videoProcess = videoPb.start();
//            user.setVideoProcess(videoProcess);
//
//            // 音频进程 (libopus, payload 111)
//            ProcessBuilder audioPb = new ProcessBuilder(
//                    "ffmpeg", "-re", "-i", filePath,
//                    "-map", "0:a", "-c:a", "libopus", "-ar", "48000", "-ac", "2", "-b:a", "128k",
//                    "-payload_type", "111", "-ssrc", String.valueOf(audioSsrc),
//                    "-f", "rtp", "rtp://127.0.0.1:" + audioPort
//            );
//            audioPb.redirectErrorStream(true);
//            Process audioProcess = audioPb.start();
//            user.setAudioProcess(audioProcess);
//
//            log.info("Video FFmpeg started for session {}: SSRC=0x{}, port={}", session.getId(), Integer.toHexString(videoSsrc), videoPort);
//            log.info("Audio FFmpeg started for session {}: SSRC=0x{}, port={}", session.getId(), Integer.toHexString(audioSsrc), audioPort);
//            log.info("FFmpeg PIDs: video={}, audio={} for session {}", videoProcess.pid(), audioProcess.pid(), session.getId());
//
//            // 读视频输出日志
//            new Thread(() -> logFFmpegOutput(videoProcess, session.getId(), "video")).start();
//
//            // 读音频输出日志
//            new Thread(() -> logFFmpegOutput(audioProcess, session.getId(), "audio")).start();
//
//            // 监听视频进程结束
//            new Thread(() -> waitForProcess(videoProcess, session, user, "video")).start();
//
//            // 监听音频进程结束
//            new Thread(() -> waitForProcess(audioProcess, session, user, "audio")).start();
//
//        } catch (IOException e) {
//            log.error("FFmpeg start IOException for session {}: {}", session.getId(), e.getMessage(), e);
//            sendError(session, "FFmpeg start failed: " + e.getMessage());
//        }
//    }
//
//    // 其他方法同前 (logFFmpegOutput, waitForProcess, play, pause, generateSdpAnswer, extractMLines, stop, sendError, sendMessage, sendPlayEnd)
//    private void logFFmpegOutput(Process process, String sessionId, String type) {
//        try (java.util.Scanner scanner = new java.util.Scanner(process.getInputStream())) {
//            while (scanner.hasNextLine()) {
//                String line = scanner.nextLine();
//                log.info("FFmpeg {} output for session {}: {}", type, sessionId, line);
//            }
//        } catch (Exception e) {
//            log.error("FFmpeg {} log read failed for session {}", type, sessionId, e);
//        }
//    }
//
//    private void waitForProcess(Process process, WebSocketSession session, UserSession user, String type) {
//        try {
//            int exitCode = process.waitFor();
//            log.info("FFmpeg {} exited with code {} for session {}", type, exitCode, session.getId());
//            if (exitCode != 0) {
//                sendError(session, "FFmpeg " + type + " exited with error code " + exitCode);
//            }
//            // 视频结束触发 playEnd
//            if (type.equals("video") && user.getAudioProcess() != null) {
//                sendPlayEnd(session);
//            }
//        } catch (InterruptedException e) {
//            Thread.currentThread().interrupt();
//        }
//    }
//
//    private void play(WebSocketSession session) {
//        log.info("Play called for session {} (loaded file, stream started)", session.getId());
//    }
//
//    private void pause(WebSocketSession session) {
//        UserSession user = users.get(session.getId());
//        if (user != null) {
//            if (user.getVideoProcess() != null) {
//                user.getVideoProcess().destroyForcibly();
//            }
//            if (user.getAudioProcess() != null) {
//                user.getAudioProcess().destroyForcibly();
//            }
//            log.info("FFmpeg paused (stopped) for session {}", session.getId());
//        } else {
//            log.warn("No processes to pause for session {}", session.getId());
//        }
//    }
//
//    private String generateSdpAnswer(int audioPort, int videoPort) {
//        String fingerprint = "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:FC:D4:9A:A2:6B:2E:8F:1F:1E:0F:0C:FC:59:7F";
//        String ufrag = "dummyufragtest";
//        String pwd = "dummypwd32charslongtest";
//
//        return "v=0\r\n" +
//                "o=- 0 0 IN IP4 127.0.0.1\r\n" +
//                "s=-\r\n" +
//                "c=IN IP4 127.0.0.1\r\n" +
//                "t=0 0\r\n" +
//                "a=group:BUNDLE 0 1\r\n" +
//                "a=msid-semantic: WMS *\r\n" +
//                "a=ice-ufrag:" + ufrag + "\r\n" +
//                "a=ice-pwd:" + pwd + "\r\n" +
//                "a=fingerprint:sha-256 " + fingerprint + "\r\n" +
//                "a=setup:passive\r\n" +
//                "m=audio " + audioPort + " UDP/TLS/RTP/SAVPF 111\r\n" +
//                "a=mid:0\r\n" +
//                "a=rtpmap:111 opus/48000/2\r\n" +
//                "a=sendrecv\r\n" +
//                "a=rtcp-mux\r\n" +
//                "a=rtcp-rsize\r\n" +
//                "a=rtcp-fb:111 transport-cc\r\n" +
//                "m=video " + videoPort + " UDP/TLS/RTP/SAVPF 96\r\n" +
//                "a=mid:1\r\n" +
//                "a=rtpmap:96 VP8/90000\r\n" +
//                "a=sendrecv\r\n" +
//                "a=rtcp-mux\r\n" +
//                "a=rtcp-rsize\r\n" +
//                "a=rtcp-fb:96 goog-remb\r\n" +
//                "a=rtcp-fb:96 transport-cc\r\n" +
//                "a=rtcp-fb:96 ccm fir\r\n" +
//                "a=rtcp-fb:96 nack\r\n" +
//                "a=rtcp-fb:96 nack pli\r\n";
//    }
//
//    private String extractMLines(String sdp) {
//        return sdp.lines().filter(line -> line.startsWith("m=")).collect(Collectors.joining("\n"));
//    }
//
//    private void stop(WebSocketSession session) {
//        UserSession user = users.remove(session.getId());
//        if (user != null) {
//            user.release();
//            log.info("Session {} stopped and resources released", session.getId());
//        }
//    }
//
//    private void sendError(WebSocketSession session, String msg) {
//        log.warn("Sending error to session {}: {}", session.getId(), msg);
//        JsonObject response = new JsonObject();
//        response.addProperty("id", "error");
//        response.addProperty("message", msg);
//        sendMessage(session, response);
//    }
//
//    private void sendMessage(WebSocketSession session, JsonObject json) {
//        try {
//            session.sendMessage(new TextMessage(json.toString()));
//            log.debug("Message sent to session {}: {}", session.getId(), json);
//        } catch (IOException e) {
//            log.error("Send failed for session {}", session.getId(), e);
//        }
//    }
//
//    private void sendPlayEnd(WebSocketSession session) {
//        log.info("Sending playEnd to session {}", session.getId());
//        JsonObject response = new JsonObject();
//        response.addProperty("id", "playEnd");
//        sendMessage(session, response);
//    }
//}